﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace Ssrtr
{
    // Separeted class to receive RDP
    public class ReceiveRDP
    {
        // State information used in the task. 
        private string xID;
        private int xPort;
        bool xDetail = false;           // Whether or not to print off the packet header information to the console
        bool xOutputRDP = false;
        bool xOutputTXT = true;
        bool xECHO = false;
        string xPath = "c:\\";
        private MemoryStream stream;
        StringBuilder textFile = new StringBuilder();
        int xStreamTypeFile = -1;
        UdpClient xclient;
        IPEndPoint endPoint;
        bool listening = true;
        int numPackets = 0;
        int oldSequence = 0;
        int oldPayload = 0;
        System.IO.StreamWriter xFile = null;

        List<long> R;
        List<long> S;
        Boolean notZero = false;

        string Sequence = "Yes";


       /// <summary>
       /// Class for receiving UDP 
       /// </summary>
       /// <param name="tID"></param>
       /// <param name="tPort"></param>
       /// <param name="tDetail"></param>
       /// <param name="tOutputRDP"></param>
       /// <param name="tOutputTXT"></param>
       /// <param name="tECHO"></param>
       /// <param name="tPath"></param>
        public ReceiveRDP(string tID, int tPort, bool tDetail, bool tOutputRDP, bool tOutputTXT, bool tECHO, string tPath)
        {
            xID = tID;
            xPort = tPort;
            xECHO = tECHO;
            xPath = tPath;
            xOutputRDP = tOutputRDP;
            xOutputTXT = tOutputTXT;
            xDetail = tDetail;
            stream = new MemoryStream();    // Memory stream which we will write the RTP packet data to
            R = new List<long>();
            S = new List<long>();

            endPoint = new IPEndPoint(IPAddress.Any, xPort);
            xclient = new UdpClient(endPoint);
        }

        /// <summary>
        /// Strat receiving UDP class
        /// </summary>
        public void startRDP()
        {

            if (xOutputTXT)
            {
                try
                {
                    xFile = new System.IO.StreamWriter(@xPath + xID + ".txt");
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error:" + e.ToString());
                    Console.ReadLine();
                    return;
                }
            }

            xclient.BeginReceive(new AsyncCallback(ThreadProc), endPoint);

            while (listening)
            {
                Thread.Sleep(100);
            }

            // End of RTP Stream, force
            try
            {
                xclient.Close();
            }
            catch (Exception) //Error of disposed object
            {
            }

            // End of RTP Stream, force exit and display.
            Console.WriteLine("{0} -> Rdp   (Total : Packets: {1}, Sequencial: {2})",
                    xID, numPackets, Sequence);


            // jitter calculation
            double max = 0;
            double average = 0;
            double jitter = 0;
            double jitter_old = 0;
            double auxiliar = 0;
            int counting = 1;
            for (int i = 1; i < R.Count; i++) // Loop through List with for
            {
                if (S[i] > 0)
                {
                    double x1 = R[i];
                    double x2 = R[i - 1];
                    x1 = (x1 - x2);
                    x1 = x1 / 1000;
                    double x3 = S[i] * 0.000125;
                    double x4 = S[i - 1] * 0.000125;
                    x3 = x3 - x4;
                    auxiliar = x1 - x3;
                    if (auxiliar < 0) auxiliar = auxiliar * (-1);
                    if (i == 1)
                    {
                        jitter = auxiliar / 16;
                    }
                    else
                    {
                        jitter = jitter_old + ((auxiliar - jitter_old) / 16);
                    }
                    jitter_old = jitter;
                    if (jitter > max) max = jitter;
                    average = average + jitter;
                    counting++;
                }
            }
            average = (average / counting) * 1000;
            max = max * 1000;

            Console.WriteLine("{0} -> Rdp   (Jitter : Average: {1}ms, Maximum: {2}ms)",
                    xID, Math.Round(average, 2), Math.Round(max, 2));


            listening = false;
            if (xOutputTXT)
            {
                xFile.WriteLine("{0} -> Rdp   (Total : Packets: {1}, Sequencial: {2})",
                    xID, numPackets, Sequence);
                xFile.Close();
            }
            if (xOutputRDP)
            {
                byte[] my = stream.ToArray();

                short[] wave = new short[my.Length];
                for (long i = 0; i < my.Length; i++)
                {
                    if (xStreamTypeFile == 0) wave[i] = MuLawDecoder.MuLawDecode(my[i]);
                    if (xStreamTypeFile == 8) wave[i] = ALawDecoder.ALawDecode(my[i]);
                }
                WaveSaver.SaveToWave(wave, xPath + xID + ".wav");
            }
            stream.Close();
            stream.Dispose();

        }

        /// <summary>
        /// 
        /// </summary>
        public void stopRDP()
        {
            listening = false;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="ar"></param>
        public void ThreadProc(IAsyncResult ar)
        {
            if (notZero)
            {

            }
            else
            {
                notZero = true;
            }


            byte[] packet;

            try
            {
                packet = xclient.EndReceive(ar, ref endPoint);

            }
            catch (Exception)
            {
                return;
            }

            long numOfTicks = DateTime.Now.Ticks;
            long numOfMilliseconds = numOfTicks / 10000;
            R.Add(numOfMilliseconds);


            // Count one packet in global variable
            numPackets++;

            // Decode the header of the packet.
            // Each piece of information has a start bit and an end bit.
            // The GetRTPHeaderValue takes the packet, the start bit index and the
            // end bit index and determines what the header value is. The header values
            // are all in Big Endian, which makes things more complicated.
            int version = GetRTPHeaderValue(packet, 0, 1);
            int padding = GetRTPHeaderValue(packet, 2, 2);
            int extension = GetRTPHeaderValue(packet, 3, 3);
            int csrcCount = GetRTPHeaderValue(packet, 4, 7);
            int marker = GetRTPHeaderValue(packet, 8, 8);
            int payloadType = GetRTPHeaderValue(packet, 9, 15);
            int sequenceNum = GetRTPHeaderValue(packet, 16, 31);
            long timestamp = GetRTPHeaderValue(packet, 32, 63);
            int ssrcId = GetRTPHeaderValue(packet, 64, 95);

            if (payloadType == 8 || payloadType == 0 || payloadType == 3) S.Add(timestamp); else S.Add(0);
            // detect type of stream in the RTP (0 = uLaw , 8 = aLaw, 3=Gsm)
            if (xStreamTypeFile < 0)
            {
                if (payloadType == 8 || payloadType == 0)
                {
                    xStreamTypeFile = payloadType;
                }
            }


            if (oldSequence != 0)
            {
                if (((oldSequence + 1) != sequenceNum) && oldSequence != sequenceNum)
                {
                    if (payloadType == oldPayload)
                    {
                        Sequence = "No";
                    }
                }
            }

            if (xDetail)
            {
                Console.WriteLine("{0} -> Rdp   {1} {2} {3} {4} {5} {6} {7} {8} {9}",
                xID, version, padding, extension, csrcCount, marker, payloadType,
                sequenceNum, timestamp, ssrcId);
            }
            if (xOutputTXT)
            {

                xFile.WriteLine("{0} -> Rdp   {1} {2} {3} {4} {5} {6} {7} {8} {9}",
                xID, version, padding, extension, csrcCount, marker, payloadType,
                sequenceNum, timestamp, ssrcId);
            }
            if (xECHO)
            {
                try
                {
                    // change the SSRC unique ID (at 8 byte or 64 bit)
                    Buffer.BlockCopy(Encoding.ASCII.GetBytes("7997"), 0, packet, 8, 4); 
                    // send the packets

                    xclient.Send(packet, packet.Length, endPoint);
         
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }


            // Write rtp stream to memmory
            stream.Write(packet, 12, packet.Length - 12);
            // call asynch receive method again
            xclient.BeginReceive(new AsyncCallback(ThreadProc), endPoint);

        }


        // get specific bytes from RTP Packet
        private int GetRTPHeaderValue(byte[] packet, int startBit, int endBit)
        {
            if (packet.Length == 2)
            {
                return 9;
            }
            int result = 0;

            // Number of bits in value
            int length = endBit - startBit + 1;

            // Values in RTP header are big endian, so need to do these conversions
            for (int i = startBit; i <= endBit; i++)
            {
                int byteIndex = i / 8;
                int bitShift = 7 - (i % 8);
                result += ((packet[byteIndex] >> bitShift) & 1) * (int)Math.Pow(2, length - i + startBit - 1);
            }
            return result;
        }


        public static void SaveMemoryStream(MemoryStream ms, string FileName)
        {
            FileStream outStream = File.OpenWrite(FileName);
            ms.WriteTo(outStream);
            outStream.Flush();
            outStream.Close();
        }


    }
}
